﻿package tk.mystudio.web.security;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

import org.apache.log4j.Logger;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.AntUrlPathMatcher;
import org.springframework.security.web.util.UrlMatcher;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import tk.mystudio.permission.bean.Resource;
import tk.mystudio.permission.bean.Role;
import tk.mystudio.permission.constant.RoleType;
import tk.mystudio.permission.service.ResourceService;
import tk.mystudio.permission.service.RoleService;


/* 
 *  
 * 最核心的地方，就是提供某个资源对应的权限定义，即getAttributes方法返回的结果。 
 * 注意，我例子中使用的是AntUrlPathMatcher这个path matcher来检查URL是否与资源定义匹配， 
 * 事实上你还要用正则的方式来匹配，或者自己实现一个matcher。 
 *  
 * 此类在初始化时，应该取到所有资源及其对应角色的定义 
 *  
 * 说明：对于方法的spring注入，只能在方法和成员变量里注入， 
 * 如果一个类要进行实例化的时候，不能注入对象和操作对象， 
 * 所以在构造函数里不能进行操作注入的数据。 
 */
@Service("invocationSecurityMetadataSourceService")
public class InvocationSecurityMetadataSourceService implements
		FilterInvocationSecurityMetadataSource, Observer {
	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger
			.getLogger(InvocationSecurityMetadataSourceService.class);

	private RoleService roleService;
	private ResourceService resourceService;

	private UrlMatcher urlMatcher = new AntUrlPathMatcher();
	private static Map<String, Collection<ConfigAttribute>> resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
	
	public void init() {
		logger.info("====== 装载权限配置 ======");
		try {
			loadResourceDefine();
		} catch (Exception e) {
			logger.error("xxxxxx 权限配置装载失败!!!");
			logger.error("xxxxxx ", e);
		}
		logger.info("====== 注册授权变更监听器 ======");
		((Observable)roleService).addObserver(this);
		logger.info("====== 注册资源变更监听器 ======");
		((Observable)resourceService).addObserver(this);
	}

	public void loadResourceDefine() throws Exception {
		Map<String, Collection<ConfigAttribute>> tmpResourceMap = new HashMap<String, Collection<ConfigAttribute>>();

		// 通过数据库中的信息设置，resouce和role
		for (Role role : roleService.getAllRoles()) {
			Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
			ConfigAttribute ca = new SecurityConfig(role.getName());
			atts.add(ca);

			List<Resource> resourceList = role.getResources();
			// 把资源放入到spring security的resouceMap中
			for (Resource resource : resourceList) {
				logger.info("获得角色：[" + role.getName() + "]拥有的acton有："
						+ resource.getUrl());
				tmpResourceMap.put(resource.getUrl(), atts);
			}
		}
		
		
		resourceMap = tmpResourceMap;
	}

	/**
	 * 去掉URL后的参数
	 * @param url
	 * @return
	 */
	private String dropParams(String url) {
		if(StringUtils.hasText(url)) {
			int index = url.indexOf("?");
			if(index!=-1) {
				return url.substring(0, index);
			}
			return url;
		}
		return "";
	}

	// According to a URL, Find out permission configuration of this URL.
	public Collection<ConfigAttribute> getAttributes(Object object)
			throws IllegalArgumentException {
		if (logger.isDebugEnabled()) {
			logger.debug("getAttributes(Object) - start"); //$NON-NLS-1$  
		}
		// guess object is a URL.
		String url = dropParams(((FilterInvocation) object).getRequestUrl());
		Iterator<String> ite = resourceMap.keySet().iterator();
		while (ite.hasNext()) {
			String resURL = ite.next();
			if (urlMatcher.pathMatchesUrl(url, resURL)) {
				Collection<ConfigAttribute> returnCollection = resourceMap
						.get(resURL);
				if (logger.isDebugEnabled()) {
					logger.debug("getAttributes(Object) - end"); //$NON-NLS-1$  
				}
				return returnCollection;
			}
		}
		if (logger.isDebugEnabled()) {
			logger.debug("getAttributes(Object) - end"); //$NON-NLS-1$  
		}
		
		Collection<ConfigAttribute> superAdmin = new ArrayList<ConfigAttribute>();
		for(Role role : roleService.getAllRoles()) {
			if(role.getRoleType()==RoleType.BUILT_IN_ROLE) {
				superAdmin.add(new SecurityConfig(role.getName()));
			}
		}
		return superAdmin;
	}

	public boolean supports(Class<?> clazz) {
		return true;
	}

	public Collection<ConfigAttribute> getAllConfigAttributes() {
		List<ConfigAttribute> config = new ArrayList<ConfigAttribute>();
		for(Role role : roleService.getAllRoles()) {
			config.add(new SecurityConfig(role.getName()));
		}
		return config;
	}
	
	@Override
	public void update(Observable o, Object obj) {
		while (true) {
			try {
				loadResourceDefine();
				logger.info("===== 授权数据刷新成功 ======");
				break;
			} catch (Exception e) {
				logger.error("xxxxxx 授权数据刷新失败,5秒后重试...");
				try {
					Thread.sleep(5000);
				} catch (InterruptedException ex) {
					
				}
			}
		}
	}
	
	public RoleService getRoleService() {
		return roleService;
	}

	public void setRoleService(RoleService roleService) {
		this.roleService = roleService;
	}

	public ResourceService getResourceService() {
		return resourceService;
	}

	public void setResourceService(ResourceService resourceService) {
		this.resourceService = resourceService;
	}

}
